package com.liuqianfei.markdown;

/**
 * markdown块结构和操作
 * 
 */
public class Block
{
    /**
     * 块的类型
     */
    public BlockType type = BlockType.NONE;
    /**
     * Head and tail of linked lines.
     */
    public Line lines = null, lineTail = null;
    /**
     * Head and tail of child blocks.
     */
    public Block blocks = null, blockTail = null;
    /**
     * 下一个块
     */
    public Block next = null;
    /**
     * 标题深度
     */
    public int hlDepth = 0;
    /**
     * 标题行或列表元素id
     */
    public String id = null;
    /**
     * 块的类
     */
    public String meta = "";

    public Block()
    {
    }

    /**
     * @return 如果块包含行，则返回<code>true</code>
     */
    public boolean hasLines()
    {
        return this.lines != null;
    }

    /**
     * 删除块前后空行
     */
    public void removeSurroundingEmptyLines()
    {
        if (this.lines != null)
        {
            this.removeTrailingEmptyLines();
            this.removeLeadingEmptyLines();
        }
    }

    /**
     * 设置标题深度<code>hlDepth</code>，同时注意字符'#'。
     */
    public void transfromHeadline()
    {
        if (this.hlDepth > 0)
        {
            return;
        }
        int level = 0;
        final Line line = this.lines;
        if (line.isEmpty)
        {
            return;
        }
        int start = line.leading;
        while (start < line.value.length() && line.value.charAt(start) == '#')
        {
            level++;
            start++;
        }
        while (start < line.value.length() && line.value.charAt(start) == ' ')
        {
            start++;
        }
        if (start >= line.value.length())
        {
            line.setEmpty();
        }
        else
        {
            int end = line.value.length() - line.trailing - 1;
            while (line.value.charAt(end) == '#')
            {
                end--;
            }
            while (line.value.charAt(end) == ' ')
            {
                end--;
            }
            line.value = line.value.substring(start, end + 1);
            line.leading = line.trailing = 0;
        }
        this.hlDepth = Math.min(level, 6);
    }

    /**
     * Used for nested lists. Removes list markers and up to 4 leading spaces.
     *
     * @param configuration 配置
     *
     */
    public void removeListIndent(final Configuration configuration)
    {
        Line line = this.lines;
        while (line != null)
        {
            if (!line.isEmpty)
            {
                switch (line.getLineType(configuration))
                {
                    case UL:
                        line.value = line.value.substring(line.leading + 2);
                        break;
                    case OL:
                        line.value = line.value.substring(line.value.indexOf('.') + 2);
                        break;
                    default:
                        line.value = line.value.substring(Math.min(line.leading, 4));
                        break;
                }
                line.initLeading();
            }
            line = line.next;
        }
    }

    /**
     * Used for nested block quotes. Removes '>' char.
     */
    public void removeBlockQuotePrefix()
    {
        Line line = this.lines;
        while (line != null)
        {
            if (!line.isEmpty)
            {
                if (line.value.charAt(line.leading) == '>')
                {
                    int rem = line.leading + 1;
                    if (line.leading + 1 < line.value.length() && line.value.charAt(line.leading + 1) == ' ')
                    {
                        rem++;
                    }
                    line.value = line.value.substring(rem);
                    line.initLeading();
                }
            }
            line = line.next;
        }
    }

    /**
     * 删除块前空行。
     *
     * @return 如果删除了空行，则返回<code>true</code>
     */
    public boolean removeLeadingEmptyLines()
    {
        boolean wasEmpty = false;
        Line line = this.lines;
        while (line != null && line.isEmpty)
        {
            this.removeLine(line);
            line = this.lines;
            wasEmpty = true;
        }
        return wasEmpty;
    }

    /**
     * 删除块后空行
     */
    public void removeTrailingEmptyLines()
    {
        Line line = this.lineTail;
        while (line != null && line.isEmpty)
        {
            this.removeLine(line);
            line = this.lineTail;
        }
    }

    /**
     * 从块中删除所给的指定行
     *
     * @param line 要被删除处理的行
     */
    public void removeLine(final Line line)
    {
        if (line.prev == null)
        {
            this.lines = line.next;
        }
        else
        {
            line.prev.next = line.next;
        }
        if (line.next == null)
        {
            this.lineTail = line.prev;
        }
        else
        {
            line.next.prev = line.prev;
        }
        line.prev = line.next = null;
    }

    /**
     * Splits this block's lines, creating a new child block having 'line' as
     * it's lineTail.
     *
     * @param line The line to split from.
     * @return The newly created Block.
     */
    public Block split(final Line line)
    {
        final Block block = new Block();

        block.lines = this.lines;
        block.lineTail = line;
        this.lines = line.next;
        line.next = null;
        if (this.lines == null)
        {
            this.lineTail = null;
        }
        else
        {
            this.lines.prev = null;
        }

        if (this.blocks == null)
        {
            this.blocks = this.blockTail = block;
        }
        else
        {
            this.blockTail.next = block;
            this.blockTail = block;
        }

        return block;
    }

    /**
     * 把给定的行追加到当前块后
     *
     * @param 要追加的行
     */
    public void appendLine(final Line line)
    {
        if (this.lineTail == null)
        {
            this.lines = this.lineTail = line;
        }
        else
        {
            this.lineTail.nextEmpty = line.isEmpty;
            line.prevEmpty = this.lineTail.isEmpty;
            line.prev = this.lineTail;
            this.lineTail.next = line;
            this.lineTail = line;
        }
    }

    /**
     * Changes all Blocks of type <code>NONE</code> to <code>PARAGRAPH</code> if
     * this Block is a List and any of the ListItems contains a paragraph.
     */
    public void expandListParagraphs()
    {
        if (this.type != BlockType.ORDERED_LIST && this.type != BlockType.UNORDERED_LIST)
        {
            return;
        }
        Block outer = this.blocks, inner;
        boolean hasParagraph = false;
        while (outer != null && !hasParagraph)
        {
            if (outer.type == BlockType.LIST_ITEM)
            {
                inner = outer.blocks;
                while (inner != null && !hasParagraph)
                {
                    if (inner.type == BlockType.PARAGRAPH)
                    {
                        hasParagraph = true;
                    }
                    inner = inner.next;
                }
            }
            outer = outer.next;
        }
        if (hasParagraph)
        {
            outer = this.blocks;
            while (outer != null)
            {
                if (outer.type == BlockType.LIST_ITEM)
                {
                    inner = outer.blocks;
                    while (inner != null)
                    {
                        if (inner.type == BlockType.NONE)
                        {
                            inner.type = BlockType.PARAGRAPH;
                        }
                        inner = inner.next;
                    }
                }
                outer = outer.next;
            }
        }
    }

}
